package ui.widget

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import models.DateFactorModel
import org.jetbrains.skia.Color
import org.jetbrains.skia.Font
import org.jetbrains.skia.Paint
import org.jetbrains.skia.TextLine
import utils.MathUtils
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.format.DateTimeFormatter

typealias ComposePaint = androidx.compose.ui.graphics.Paint
typealias ComposeColor = androidx.compose.ui.graphics.Color

/**
 *
 * 日期选择器，仿照material design 官方控件
 * @param onDateSelected 点击某日期后，该日期的回调函数
 *
 * */
@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
@Composable
fun DatePicker(onDateSelected: (LocalDate) -> Unit) {
//    val today = LocalDate.now()
//    //确定这个月有多少天
//    val daysOfMonth = today.lengthOfMonth()
    var monthOffset by remember { mutableStateOf(0) }
    val now = LocalDate.now()
    val dateList = remember {
        mutableStateListOf(
            DateFactorModel(now.plusMonths((-1 + monthOffset).toLong()),-1f),
            DateFactorModel(now.plusMonths(monthOffset.toLong()),0f),
            DateFactorModel(now.plusMonths((1 + monthOffset).toLong()),1f)
        )
    }
    var buttonRelease by remember { mutableStateOf(true) }
    var pointLocation by remember { mutableStateOf(Offset(0f, 0f)) }
    var showSelected by remember { mutableStateOf(false) }
    var dragOffset by remember { mutableStateOf(Offset(0f, 0f)) }
    var canDrag by remember { mutableStateOf(true) }
    var startAnimate by remember { mutableStateOf(false) }
    var offsetFactor by remember { mutableStateOf(0f) }
    var dragBackward by remember { mutableStateOf(false) }
    val dragState = rememberDraggableState {
        showSelected = false
        dragOffset = Offset(it + dragOffset.x, 0f)
        dragBackward = it < 0
    }
    val animateOffset by animateFloatAsState(
        offsetFactor,
        finishedListener = {
            if (offsetFactor != 0f){
                if (dragBackward) monthOffset ++ else  monthOffset --
                dateList.clear()
                dateList.addAll(listOf(DateFactorModel(now.plusMonths((-1 + monthOffset).toLong()),-1f),
                    DateFactorModel(now.plusMonths(monthOffset.toLong()),0f),
                    DateFactorModel(now.plusMonths((1 + monthOffset).toLong()),1f)))
                dateList.forEach { dateFactorModel -> println("列表里面的日期：" + dateFactorModel.date.format(DateTimeFormatter.ISO_DATE)) }
                dragOffset = Offset(0f, 0f)
                startAnimate = false
                canDrag = true
            }
            offsetFactor = 0f
        }
    )
    Box(modifier = Modifier.fillMaxSize())
    {
        Canvas(modifier = Modifier.fillMaxSize()
            .onPointerEvent(PointerEventType.Press) {
                val position = it.changes.first().position
                pointLocation = Offset(position.x, position.y)
                showSelected = true
                buttonRelease = false
            }
            .onPointerEvent(PointerEventType.Release)
            {
                buttonRelease = true
            }
            .draggable(state = dragState, orientation = Orientation.Horizontal, onDragStopped = {
                if (buttonRelease)
                {
                    startAnimate = true
                    offsetFactor = if (dragBackward) 1f else  -1f
                    canDrag = false
                }
            }, enabled = canDrag)
//            .pointerInput(Unit)
//            {
//                detectHorizontalDragGestures(
//                    onHorizontalDrag = { _, dragAmount ->
//                        showSelected = false
//                        dragOffset = Offset(dragAmount + dragOffset.x, 0f)
//                        if (dragOffset.x < 0) dragBackward = true
//                        else if (dragOffset.x > 0) dragBackward = false
//                    },
//                    onDragEnd = {
//                        startAnimate = true
//                        offsetFactor = if (dragBackward)   1f else  -1f
//                    }
//                )
//            }


        ) {
            val widthPerDay = size.width / 7
            val paint = Paint().apply {
                color = Color.BLACK
            }
            val fontSize = 20f
            val font = Font(null, fontSize)
            fun drawMonth(month: LocalDate, offset: Float) {
                var initialHeight = widthPerDay / 2
                val daysOfMonth = month.lengthOfMonth()
                val dayXLocations = (0 until 7).map {
                    (it + 0.5f) * widthPerDay + dragOffset.x + offset + if(startAnimate)(animateOffset*(size.width - dragOffset.x)) else 0f
                }
                val positionDic = (1..daysOfMonth).associateWith {
                    val thisDay = LocalDate.of(month.year, month.monthValue, it)
                    if (it != 1 && thisDay.dayOfWeek == DayOfWeek.MONDAY) initialHeight += widthPerDay
                    val text = it.toString()
                    val textLine = TextLine.make(text, font)
                    Offset(
                        dayXLocations[thisDay.dayOfWeek.value - 1] - textLine.width / 2,
                        initialHeight + fontSize * 2 / 5
                    )
                }
                initialHeight = widthPerDay / 2
                val centerPositions = (1..daysOfMonth).associateWith {
                    val thisDay = LocalDate.of(month.year, month.monthValue, it)
                    if (it != 1 && thisDay.dayOfWeek == DayOfWeek.MONDAY) initialHeight += widthPerDay
                    Offset(dayXLocations[thisDay.dayOfWeek.value - 1], initialHeight)
                }

                if (showSelected) {
                    val position = centerPositions.minWithOrNull { a, b ->
                        if (MathUtils.distance(a.value, pointLocation) > MathUtils.distance(
                                b.value,
                                pointLocation
                            )
                        ) 1 else 0
                    } ?: return
                    if (position.value.x > 0 && position.value.x < size.width){
                        drawCircle(ComposeColor.Red, 20f, position.value)
                        val selectedDate = LocalDate.of(month.year, month.monthValue, position.key)
                        onDateSelected(selectedDate)
                    }
                }

                drawIntoCanvas { canvas ->
                    positionDic.forEach {
                        if (it.value.x > 0 && it.value.x < size.width)
                            canvas.nativeCanvas.drawString(it.key.toString(), it.value.x, it.value.y, font, paint)
                    }
                }
            }

            dateList.forEach {
                drawMonth(it.date,it.factor * size.width)
            }
        }
    }
}


@Preview
@Composable
fun PreviewDatePicker() {
    Box(modifier = Modifier.size(200.dp))
    {
        DatePicker { println(it.format(DateTimeFormatter.ISO_DATE)) }
    }

}